package cn.cjx.basic.service.impl;

import cn.cjx.basic.constant.BaseConstant;
import cn.cjx.basic.domain.LoginData;
import cn.cjx.basic.dto.LoginDto;
import cn.cjx.basic.dto.WechatDto;
import cn.cjx.basic.exception.BusinessException;
import cn.cjx.basic.service.ILoginService;
import cn.cjx.basic.utils.*;
import cn.cjx.system.domain.Menu;
import cn.cjx.system.mapper.MenuMapper;
import cn.cjx.system.mapper.PermissionMapper;
import cn.cjx.user.domain.Logininfo;
import cn.cjx.user.domain.User;
import cn.cjx.user.domain.Wxuser;
import cn.cjx.user.mapper.LogininfoMapper;
import cn.cjx.user.mapper.UserMapper;
import cn.cjx.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;



/**
 * @author: cjx
 * @Date: 2022/5/10
 */
@Service
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public class LoginServiceImpl implements ILoginService {

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private LogininfoMapper logininfoMapper;

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private PermissionMapper permissionMapper;

	@Autowired
	private MenuMapper menuMapper;


	@Autowired
	private WxuserMapper wxuserMapper;

	/**
	 * @author 陈吉祥
	 * @Date 2022/5/13
	 * 账号密码登录
	 * 保存到redis，30分钟内账号此次登录有效
	 */
	@Override
	public HashMap<String, Object> loginAccount(LoginDto dto) {

		//1.空校验
		if(StringUtils.isEmpty(dto.getUsername())
				|| StringUtils.isEmpty(dto.getPassword())
				|| StringUtils.isEmpty(dto.getType()))
		{
			throw new BusinessException("参数不能为空！！！");
		}
		//2.根据得到的username + type在t_logininfo查询用户数据
		// 在这里我们username要去匹配username，email，phone
		Logininfo logininfoTemp = logininfoMapper.loadByAccountAndType(dto.getUsername(), dto.getType());
		//判断用户状态
		if (logininfoTemp.getDisable() == false) {
			throw new BusinessException("由于非法操作，该账户已被禁用，请文明使用账号！");
		}

		//3.数据库中没有查询到该用户
		if (logininfoTemp == null) {
			throw new BusinessException("用户名或密码错误");
		}

		//4.数据库中查询到了该用户，比对密码
		String salt = logininfoTemp.getSalt();
		String md5Pwd = MD5Utils.encrypByMd5(dto.getPassword() + "cjx" + salt + "xxxx");
		if (!logininfoTemp.getPassword().equals(md5Pwd)) {

			throw new BusinessException("用户名或密码错误");
		}


		//保存token + 用户基本信息到redis
//		String token = UUID.randomUUID().toString();
//		redisTemplate.opsForValue().set(token, logininfoTemp, 30, TimeUnit.MINUTES);

		/*//返回 token + logininfo给前端
		HashMap<String, Object> map = new HashMap<>();
		map.put("token", token);
		map.put("logininfo", logininfoTemp);
		logininfoTemp.setSalt(null);
		logininfoTemp.setPassword(null);
		return map;*/
		return initLoginHandler(logininfoTemp);
	}

	private HashMap<String, Object> initLoginHandler(Logininfo logininfoTemp) {
		LoginData loginData = new LoginData();
		loginData.setLogininfo(logininfoTemp);
		if (logininfoTemp.getType()==0) {

			//。查询出当前登录人所拥有的资源权限
			List<String> permissions = permissionMapper.loadPermissionByLogininfoId(logininfoTemp.getId());
			List<Menu> menus = menuMapper.loadMenuByLogininfoId(logininfoTemp.getId());
			//返回菜单权限和按钮权限
			loginData.setMenus(menus);
			loginData.setPermissions(permissions);
		}
		//使用私钥加密
		try {
			PrivateKey privateKey = RsaUtils.getPrivateKey(JwtUtils.class.getClassLoader().getResource("hrm_auth_rsa").getFile());
			String jwtStr = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 60);
			//使用map来接收我需要传递到前端的数据，
			//用户登录信息，加密后的密文，以及没加密的用户菜单权限与按钮权限
			//之所以还要传明文，原因是前端解密太过麻烦，先不做后期 TODO
			HashMap<String, Object> map = new HashMap<>();
			logininfoTemp.setSalt(null);
			logininfoTemp.setPassword(null);
			map.put("token",jwtStr);
			map.put("logininfo",logininfoTemp);
			map.put("menus",loginData.getMenus());
			map.put("permissions",loginData.getPermissions());
			return map;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}


	/**
	 * @author 陈吉祥
	 * @Date 2022/5/13
	 * 微信扫码登录
	 * 发起第二次请求。携带code返回token和openid
	 * 若用户是第一次扫码登录，则跳转到绑定页面
	 * 第二次的话直接免密登录
	 */
	@Override
	public AjaxResult wechatLogin(WechatDto dto) {
		String code = dto.getCode();
		//1.空校验
		if (StringUtils.isEmpty(code)) {
			throw new BusinessException("授权失败，请稍后重试");
		}
		//2.拼接微信提供的生成授权码Code的URL，根据code，发送http请求从微信获取token
		String tokenUrl = BaseConstant.WxConstants.GET_TOKEN_URL
				.replace("APPID", BaseConstant.WxConstants.APPID)
				.replace("SECRET", BaseConstant.WxConstants.SECRET)
				.replace("CODE", code);
		//向微信发送HTTP请求，得到json字符串格式的响应
		String jsonStr = HttpClientUtils.httpGet(tokenUrl);
		//把json格式字符串的响应转换为JSONObject对象
		JSONObject jsonObject = JSONObject.parseObject(jsonStr);
		//3.得到String类型的token和openid，根据openId查询t_wxuser表
		String accessToken = jsonObject.getString("access_token");
		String openId = jsonObject.getString("openid");
		//4.根据openId查询t_wxuser表
		Wxuser wxuser = wxuserMapper.loadByOpenId(openId);
		//5.是否wxUser存在且绑定了userId
		if (wxuser != null && wxuser.getUserId() != null) {

			Logininfo logininfoTemp = logininfoMapper.loadByUserId(wxuser.getUserId());
			//保存token + 用户基本信息到redis
			/*	String token = UUID.randomUUID().toString();
			redisTemplate.opsForValue().set(token, logininfoTemp, 30, TimeUnit.MINUTES);

			//返回 token + logininfo给前端,免密登录成功
			HashMap<String, Object> map = new HashMap<>();
			map.put("token", token);
			logininfoTemp.setSalt(null);
			logininfoTemp.setPassword(null);
			map.put("logininfo", logininfoTemp);*/
			HashMap<String, Object> map = initLoginHandler(logininfoTemp);
			return AjaxResult.success().setResultObj(map);
		}
		//没有查询到，即用户是第一次扫码登录，应该定位到绑定页面，让用户自己输入绑定账号
		String binderTmp = "?accessToken=" +accessToken+"&openId="+openId;
		HashMap<String, Object> map = new HashMap<>();
		//封装accessToken和openId并返回给前端
		map.put("binderTmp", binderTmp);
		return AjaxResult.error().setResultObj(map);


	}

	/**
	 * @author 陈吉祥
	 * @Date 2022/5/13
	 * 该方法是微信绑定接口调用的
	 * 在绑定页面填好短信验证码后绑定时调用该方法
	 * 发起第三次请求从微信获取用户授权的基本信息
	 */
	@Override
	public HashMap<String, Object> wechatBinder(WechatDto dto) {

		//校验短信验证码
		String smsCode = BaseConstant.VerifyCodeConstant.BUSINESS_BINDER_PREFIX + dto.getPhone();//验证码与手机号拼接
		Object smsCodeTemp = redisTemplate.opsForValue().get(smsCode);
		if (smsCodeTemp == null) {
			throw new BusinessException("验证码无效或已过期，请重新获取");
		}
		String smsCodeVal = smsCodeTemp.toString().split(":")[0];
		if (!smsCodeVal.equalsIgnoreCase(dto.getVerifyCode())) {
			throw new BusinessException("验证码错误，请输入正确的验证码");
		}
		//1.根据username判断用户是否已经注册
		Logininfo logininfo = logininfoMapper.loadByAccountAndType(dto.getPhone(), 1);
		User user = null;
		if (logininfo == null) {
			//没有注册 生成新的t_user t_logininfo
			user = dto2User(dto.getPhone());
			logininfo = user2Logininfo(user);
			//先保存t_logininfo，然后返回自增id
			logininfoMapper.save(logininfo);
			//根据logininfoId来保存user
			user.setLogininfo_id(logininfo.getId());
			userMapper.save(user);
		}else {//注册了  查询用户 t_loiginfio
			user = userMapper.loadByLogininfoId(logininfo.getId());
		}
		String wxUserUrl = BaseConstant.WxConstants.GET_USER_URL
				.replace("ACCESS_TOKEN", dto.getAccessToken())
				.replace("OPENID", dto.getOpenId());
		//拼接token + openId 从微信获取微信的用户基本信息    ③请求
		String wxUserStr = HttpClientUtils.httpGet(wxUserUrl);
		Wxuser wxuser = JSONObject.parseObject(wxUserStr, Wxuser.class);
		//绑定t_wxuser和t_user
		wxuser.setUserId(user.getId());
		wxuserMapper.save(wxuser);
		/*//免密登录
		String token = UUID.randomUUID().toString();
		redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);

		//返回 token + logininfo给前端,免密登录成功
		HashMap<String, Object> map = new HashMap<>();
		map.put("token", token);
		logininfo.setSalt(null);
		logininfo.setPassword(null);
		map.put("logininfo", logininfo);
		return map;*/
		return initLoginHandler(logininfo);
	}
	private Logininfo user2Logininfo(User user) {
		Logininfo logininfo = new Logininfo() ;
		BeanUtils.copyProperties(user,logininfo);
		logininfo.setType(1);
		return logininfo;
	}

	private User dto2User(String phone) {
		User user = new User();
		user.setUsername(phone);
		user.setPhone(phone);

		String salt = StrUtils.getComplexRandomString(32);
		String md5Pwd = MD5Utils.encrypByMd5("1" +"cjx" + salt + "xxxx");
		/*SendMsgUtil.sendMsg(
				"15985137174",
				"尊敬的用户您好，你的账号绑定微信成功，账号初始密码为123456，为了账号安全，请尽快登录修改");*/
		System.out.println("尊敬的用户您好，你的账号绑定微信成功，账号初始密码为123456，为了账号安全，请尽快登录修改");
		user.setSalt(salt);
		user.setPassword(md5Pwd);
		return user;
	}
}
